4章 制御フロー
https://gyazo.com/51ba590422cb6160a4960060509a9941
制御フロー入門
whileループ
条件がtrueの間は処理をループ
ブロック文
ブロック文は{...}で囲まれた1文あるいは複数文でひとつの単位として扱われる。
制御フロー文と組み合わせて使う。
制御フロー文が1行であればブロック文を省略できる。(一貫性のために使うのもあり)
ホワイトスペース
JavaScriptではスペースや改行文字があっても殆どの場合は無視される
code:JavaScript
// 以下はすべて同じ
// 改行あり(スペースが空いても無視される)
while(funds > 1 && funds < 100)
funds = funds + 2;
// 改行なし
while(funds > 1 && funds < 100) funds = funds + 2;
// 改行なしで1文をブロック文に
while(funds > 1 && funds < 100) {funds = funds + 2; }
1行ならブロック文を使わなくても良いが、意図しない結果になることもある。
code:JavaScript
while(funds > 1 && funds < 100)
funds = funds + 2; // ここまでループの中
funds = funds - 1; // これはループの外
1行のみのときにブロック分を省略するかどうかは賛否が分かれるが、ひとつのif文の中にブロック文と1行の文を混在させるのはすべきではない。
if...else文
(totalBet === 7)判断ノード
elseは省略可能
do whileループ
判断するのは最後
forループ
回数が決まっているループに適している
forループの制御部は初期化、条件、再設定からなる
for(let roll = 0; roll < 3; roll++) {制御フロー}
JavaScriptの制御フロー文
制御フローは分岐とループの2つに大きく分けられる。
制御フローの例外
break: ループを途中で抜ける
continue: ループ内の今回分の繰り返し処理を終了して、次回分の繰り返し処理に進む
return: 現在の関数を制御フローに関係なく終了する
throw: 例外ハンドラでキャッチする必要がある例外を示す
if...else文のつなぎ方
else if→ else文にさらにif文があるということ
ブロック文で表現するとelse { if(true){...} else {...} }
構文の詳細
メタ構文:EBNF(Extended Backus-Naur Form: 拡張バッカス・ナウア記法)
構文を説明する際によく用いられる
[...]で囲まれているものはオプション(省略可能)
...はさらに続くという意味
code:EBNF
// while文
while(条件)
文
// if...else文
if(条件)
文1
[else
文2]
// do...While文
do
文
while(条件);
// for文
文
その他のforループパターン
カンマ演算子を使うと代入や式をまとめて記述することができる
code:JavaScript
for(let temp, i=0, j=1; j<30; temp = i, i = j, j = i +temp)
console.log(j); // 1 1 2 3 5 8 13 21
条件を指定しないと無限ループになる
code:JavaScript
for(;;) console.log("永遠に繰り返し!");
ループ変数を増減させる以外にも式なら何でも使える
code:JavaScript
let s = '3';
for(; s.length<10; s = ' ' + s;
console.log(s); // 3(空白9個+3)
条件としてオブジェクトのプロパティを使える
code:JavaScript
for(; !player.isBroke;) {
console.log("まだプレイ中です!");
}
forループはwhileループで書き直せる
code:JavaScript
// forループ
文
// whileループ
文
再設定
}
forループのメリット
ループの制御情報が全て最初の行にあるのでわかりやすい
letを使って変数を初期化することで変数のスコープがforループ内に限定される。
これをwhileで書くと制御変数が必然的にループの外側でも有効になってしまう。
switch文
3つ以上の値に分かれるような条件
break文を見つけるまで文を実行するので、同じ結果になる場合はフォールスルー(fall-through)と呼ばれる手法を取れる。
↓ではtotalBetが13だった場合、case 13の文を実行した上でcase 11の文も実行する。
code:JavaScript
switch(totalBet) {
case 7:
totalBet = funds;
break;
case 13:
funds = funds - 1;
case 11:
totalBet = 0;
break;
case 21:
totalBet = 21;
break;
default:
console.log("縁起担ぎはなし。");
break;
}
フォロースルーは誤読しがちなのでコメントを残しておく。
defaultはそれ以上分岐はないのでbreakは不要だが習慣をつけておいたほうがよい。
フォールスルーにもbreakを書いてコメントアウトする。
関数の中でswitchを使っている場合はreturnを使うことで直ちに終了することができる。
通常はbreakをswitchと同じ行に書いてコンパクトにする。
for...inループ
オブジェクトのプロパティキーに対して処理を繰り返す
code:JavaScript
for(変数 in オブジェクト)
文
for...ofループ
ES2015で追加された。
ある集合の各要素に対して繰り返し処理を行う。
code:JavaScript
for(変数 of オブジェクト)
文
for...ofループは配列に対しても使えるが、一般にイテレーション(反復)可能なオブジェクト(iterable object)に対して使える。
配列に対してループを実行する場合、配列のインデックスを知る必要はないので便利。
便利な制御フローパターン
条件の入れ子を減らすためにcontinueを使う方法
code:JavaScript
// ループ部分がネストされている
while(funds > 1 && funds < 100) {
let totalBet = rand(1, funds);
if(totalBet === 13) {
console.log("不吉だ…。今回はパス…");
} else {
// プレイする…
}
}
// continueを使って入れ子をなくしてフラットな構造にできる
while(funds > 1 && funds < 100) {
let totalBet = rand(1, funds);
if(totalBet === 13) {
console.log("不吉だ…今回はパス…");
continue;
}
// プレイする...
}
不要な計算を避けるためにbreakやreturnを使う方法
code:JavaScript
let firstPrime = null;
for(let n of bigArrayOfNumbers) {
if(isPrime(n) {
firstPrime = n;
break; //関数の中にある場合はreturn
}
}
ループ終了後のループ変数の値を使う方法
ループ終了時のループ変数の値が重要な場合がある。
ループがbreakを使うことなく正常に終了するエッジケースに注意が必要。
code:JavaScript
let i = 0;
for(; i < bigArrayOfNumbers.length; i++) {
if(isPrime(bigArrayOfNumbersi)) break; }
if(i === bigArrayOfNumbers.length) console.log('素数はなし!');
else console.log(最初の素数の発見位置: ${i});
リストを変更するときにチェックを降順に行う方法
リストの要素に対してループを実行しつつリストを変更する場合は、変更によってループの終了条件が変わる可能性に注意。
一般的な対策として、チェックの順番を降順にしてループを最後から最初に向かって進める方法がある。
code:JavaScript
for(let i=bigArrayOfNumbers.length-1; i >=0; i--) {
if(isPrime(bigArrayOfNumbersi)) bigArrayOfNumbers.splice(i,-1); }